#include "Game Engine/Header/Object.h"

Object::Object() :
mDefaultContactProcessingThreshold(BT_LARGE_FLOAT)
{
	mPosition	= D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
	mVelocity	= D3DXVECTOR3( 0.0f, 0.0f, 0.0f );

	mAngle		= 0.0f;

	D3DXMatrixIdentity( &mWorld );
	D3DXMatrixIdentity( &mChange );
}

Object::~Object()
{
}

void Object::Init( Mesh* mesh, Shape shape, bool dynamic )
{
	mMesh = mesh;
	mMeshData = mMesh->d3dxMesh();

	float mass = 0.1f;
	btCollisionShape* cs;

	//Create Motion State
	btQuaternion q(0.0f, 0.0f, 0.0f);
	btVector3 p( 0.0f, 0.0f, 0.0f );
	btTransform startTrans(q, p);
	btMotionState *ms = new btDefaultMotionState(startTrans);

	D3DXVECTOR3 min;
	D3DXVECTOR3 max;

	GetAABB( min, max );

	float sizeX = abs( max.x - min.x );
	float sizeY = abs( max.y - min.y );
	float sizeZ = abs( max.z - min.z );

	switch( shape )
	{
	case Capsule:
		{
			cs = new btCapsuleShape( sizeX, sizeY );
			break;
		}
	case Cube:
		{
			//Create Collision Shape
			cs = new btBoxShape(btVector3(sizeX * 0.5f, sizeY * 0.5f, sizeZ * 0.5f));
	
			mass = sizeX * sizeY * sizeZ;
			break;
		}
	case Cyl:
		{
			cs = new btCylinderShape( btVector3(CUBE_HALF_EXTENTS,CUBE_HALF_EXTENTS,CUBE_HALF_EXTENTS) );
			break;
		}
	case Plane:
		{
			cs = new btStaticPlaneShape(btVector3(0,1,0),-0.5);
			break;
		}
	case Sph:
		{
			cs = new btSphereShape( sizeX * 0.5f );
			break;
		}
	};

	btVector3 localInertia(0,0,0);
	if (dynamic)
		cs->calculateLocalInertia(mass,localInertia);
	else
		mass = 0.0f;

	//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects

	btRigidBody::btRigidBodyConstructionInfo cInfo(mass,ms,cs,localInertia);

	mBody = new btRigidBody(cInfo);
	mBody->setContactProcessingThreshold(mDefaultContactProcessingThreshold);

	//mBody = new btRigidBody( mass, ms, cs, localInertia );

	mBody->setCcdMotionThreshold(CUBE_HALF_EXTENTS);
	mBody->setCcdSweptSphereRadius(0.9*CUBE_HALF_EXTENTS);
}

void Object::Strafe( float d )
{
	mVelocity.x += d;
}

void Object::Walk( float d )
{
	mVelocity.z += d;
}

void Object::Rotate( float d, bool clockwise )
{
	mAngle += d * ( PI / 180.0f );
	if( mAngle >= 2 * PI )
		mAngle = 0.0f;
	D3DXMatrixRotationY( &mWorld, mAngle );
}

void Object::GetAABB(D3DXVECTOR3& vMin, D3DXVECTOR3& vMax)
{
	ID3DX10MeshBuffer* vb = 0;
	HR(mMeshData->GetVertexBuffer(0, &vb));
	
	ObjectVertex* vertices = 0;
	SIZE_T size;
	HR(vb->Map((void**)&vertices, &size));

	vMin = D3DXVECTOR3(+INFINITY, +INFINITY, +INFINITY);
	vMax = D3DXVECTOR3(-INFINITY, -INFINITY, -INFINITY);

	for(UINT i = 0; i < mMeshData->GetVertexCount(); ++i)
	{
		D3DXVec3Minimize(&vMin, &vMin, &vertices[i].pos);
		D3DXVec3Maximize(&vMax, &vMax, &vertices[i].pos);
	}
	
	HR(vb->Unmap());

	ReleaseCOM(vb);
}

void Object::FixedPosition( D3DXVECTOR3& pos )
{
	D3DXMatrixTranslation( &mChange, pos.x, pos.y, pos.z );

	mWorld *= mChange;
}

void Object::FixedRotation( float angle )
{
	mAngle += angle * ( PI / 180.0f );
	if( mAngle >= 2 * PI )
		mAngle = 0.0f;
	D3DXMatrixRotationY( &mChange, mAngle );

	mWorld *= mChange;
}

D3DXVECTOR3& Object::GetPosition()
{
	mWorld(3, 0) = mPosition.x;
	mWorld(3, 1) = mPosition.y;
	mWorld(3, 2) = mPosition.z;

	return mPosition;
}

D3DXVECTOR3& Object::GetVelocity()
{
	mVelocity.y *= 0.6f;
	mVelocity.x *= 0.6f;
	mVelocity.z *= 0.6f;

	return mVelocity;
}

D3DXMATRIX& Object::World()
{
	return mWorld;
}

btRigidBody* Object::LocalCreateRigidBody( btDynamicsWorld* world, float mass, const btTransform& startTransform, btCollisionShape* shape )
{
	btAssert((!shape || shape->getShapeType() != INVALID_SHAPE_PROXYTYPE));

	//rigidbody is dynamic if and only if mass is non zero, otherwise static
	bool isDynamic = (mass != 0.f);

	btVector3 localInertia(0,0,0);
	if (isDynamic)
		shape->calculateLocalInertia(mass,localInertia);

	//using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects

#define USE_MOTIONSTATE 1
#ifdef USE_MOTIONSTATE
	btDefaultMotionState* myMotionState = new btDefaultMotionState(startTransform);

	btRigidBody::btRigidBodyConstructionInfo cInfo(mass,myMotionState,shape,localInertia);

	btRigidBody* body = new btRigidBody(cInfo);
	body->setContactProcessingThreshold(mDefaultContactProcessingThreshold);

#else
	btRigidBody* body = new btRigidBody(mass,0,shape,localInertia);	
	body->setWorldTransform(startTransform);
#endif//

	return body;
}

btRigidBody* Object::GetBody()
{
	return mBody;
}